#include "../src/auth/auth_manager.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

// Test utility functions
static void test_print(const char* test_name, int success) {
    printf("[%s] %s\n", success ? "PASS" : "FAIL", test_name);
}

// Test authentication manager initialization
static int test_auth_initialization() {
    printf("\n=== Testing Authentication Manager Initialization ===\n");
    
    int success = 1;
    
    // Test initialization
    auth_manager_t* auth = auth_init();
    test_print("Auth Manager Initialization", auth != NULL);
    
    if (auth) {
        // Check default admin user exists
        auth_user_t* admin = auth_find_user(auth, "admin");
        test_print("Default Admin User Created", admin != NULL);
        
        if (admin) {
            bool role_correct = (strcmp(admin->role, ROLE_ADMIN) == 0);
            test_print("Admin User Role Correct", role_correct);
            if (!role_correct) success = 0;
        } else {
            success = 0;
        }
        
        // Print initial statistics
        printf("\nInitial auth manager state:\n");
        auth_print_stats(auth);
        auth_print_users(auth);
        
        // Test cleanup
        int cleanup_result = auth_cleanup(auth);
        test_print("Auth Manager Cleanup", cleanup_result == 0);
        
        if (cleanup_result != 0) {
            success = 0;
        }
    } else {
        success = 0;
    }
    
    return success;
}

// Test user management
static int test_user_management() {
    printf("\n=== Testing User Management ===\n");
    
    int success = 1;
    
    auth_manager_t* auth = auth_init();
    if (!auth) {
        printf("Failed to initialize auth manager\n");
        return 0;
    }
    
    // Test creating users with different roles
    int create_result1 = auth_create_user(auth, "alice", "password123", ROLE_READ_WRITE, HASH_TYPE_SHA256);
    test_print("Create Read-Write User", create_result1 == 0);
    
    int create_result2 = auth_create_user(auth, "bob", "password456", ROLE_READ_ONLY, HASH_TYPE_SHA256);
    test_print("Create Read-Only User", create_result2 == 0);
    
    // Test finding users
    auth_user_t* alice = auth_find_user(auth, "alice");
    test_print("Find Created User", alice != NULL);
    
    if (alice) {
        bool role_correct = (strcmp(alice->role, ROLE_READ_WRITE) == 0);
        test_print("User Role Correct", role_correct);
        if (!role_correct) success = 0;
    } else {
        success = 0;
    }
    
    // Test duplicate user creation (should fail)
    int duplicate_result = auth_create_user(auth, "alice", "newpass", ROLE_ADMIN, HASH_TYPE_SHA256);
    test_print("Duplicate User Rejection", duplicate_result != 0);
    
    if (duplicate_result == 0) success = 0;
    
    // Test user deletion
    int delete_result = auth_delete_user(auth, "bob");
    test_print("User Deletion", delete_result == 0);
    
    // Verify user was deleted
    auth_user_t* deleted_user = auth_find_user(auth, "bob");
    test_print("User Actually Deleted", deleted_user == NULL);
    
    if (deleted_user != NULL) success = 0;
    
    if (create_result1 != 0 || create_result2 != 0 || delete_result != 0) {
        success = 0;
    }
    
    printf("\nFinal user list:\n");
    auth_print_users(auth);
    
    auth_cleanup(auth);
    return success;
}

// Test authentication
static int test_authentication() {
    printf("\n=== Testing Authentication ===\n");
    
    int success = 1;
    
    auth_manager_t* auth = auth_init();
    if (!auth) {
        printf("Failed to initialize auth manager\n");
        return 0;
    }
    
    // Create test user
    auth_create_user(auth, "testuser", "testpass", ROLE_READ_WRITE, HASH_TYPE_SHA256);
    
    // Test successful authentication
    uint64_t session_id = 0;
    int auth_result = auth_authenticate(auth, "testuser", "testpass", &session_id);
    test_print("Successful Authentication", auth_result == 0 && session_id != 0);
    
    if (auth_result != 0 || session_id == 0) {
        success = 0;
    }
    
    // Test failed authentication (wrong password)
    uint64_t bad_session_id = 0;
    int bad_auth_result = auth_authenticate(auth, "testuser", "wrongpass", &bad_session_id);
    test_print("Failed Authentication (Wrong Password)", bad_auth_result != 0);
    
    if (bad_auth_result == 0) success = 0;
    
    // Test failed authentication (non-existent user)
    uint64_t nonexist_session_id = 0;
    int nonexist_result = auth_authenticate(auth, "nonexistent", "password", &nonexist_session_id);
    test_print("Failed Authentication (Non-existent User)", nonexist_result != 0);
    
    if (nonexist_result == 0) success = 0;
    
    // Test session validation
    if (session_id != 0) {
        auth_session_t* session = auth_find_session(auth, session_id);
        test_print("Session Found", session != NULL);
        
        if (session) {
            bool username_correct = (strcmp(session->username, "testuser") == 0);
            test_print("Session Username Correct", username_correct);
            if (!username_correct) success = 0;
        } else {
            success = 0;
        }
        
        // Test logout
        int logout_result = auth_logout(auth, session_id);
        test_print("Logout", logout_result == 0);
        
        // Verify session is gone
        auth_session_t* deleted_session = auth_find_session(auth, session_id);
        test_print("Session Deleted After Logout", deleted_session == NULL);
        
        if (logout_result != 0 || deleted_session != NULL) {
            success = 0;
        }
    }
    
    printf("\nAuthentication statistics:\n");
    auth_print_stats(auth);
    
    auth_cleanup(auth);
    return success;
}

// Test permissions
static int test_permissions() {
    printf("\n=== Testing Permissions ===\n");
    
    int success = 1;
    
    auth_manager_t* auth = auth_init();
    if (!auth) {
        printf("Failed to initialize auth manager\n");
        return 0;
    }
    
    // Create users with different roles
    auth_create_user(auth, "admin_user", "pass", ROLE_ADMIN, HASH_TYPE_SHA256);
    auth_create_user(auth, "rw_user", "pass", ROLE_READ_WRITE, HASH_TYPE_SHA256);
    auth_create_user(auth, "ro_user", "pass", ROLE_READ_ONLY, HASH_TYPE_SHA256);
    
    // Authenticate users
    uint64_t admin_session, rw_session, ro_session;
    auth_authenticate(auth, "admin_user", "pass", &admin_session);
    auth_authenticate(auth, "rw_user", "pass", &rw_session);
    auth_authenticate(auth, "ro_user", "pass", &ro_session);
    
    // Test admin permissions (should have all)
    int admin_select = auth_check_permission(auth, admin_session, "test_table", PERM_SELECT);
    int admin_insert = auth_check_permission(auth, admin_session, "test_table", PERM_INSERT);
    int admin_delete = auth_check_permission(auth, admin_session, "test_table", PERM_DELETE);
    
    test_print("Admin SELECT Permission", admin_select == 0);
    test_print("Admin INSERT Permission", admin_insert == 0);
    test_print("Admin DELETE Permission", admin_delete == 0);
    
    // Test read-write user permissions
    int rw_select = auth_check_permission(auth, rw_session, "test_table", PERM_SELECT);
    int rw_insert = auth_check_permission(auth, rw_session, "test_table", PERM_INSERT);
    int rw_create = auth_check_permission(auth, rw_session, "test_table", PERM_CREATE);
    
    test_print("Read-Write SELECT Permission", rw_select == 0);
    test_print("Read-Write INSERT Permission", rw_insert == 0);
    test_print("Read-Write CREATE Permission Denied", rw_create != 0);
    
    // Test read-only user permissions
    int ro_select = auth_check_permission(auth, ro_session, "test_table", PERM_SELECT);
    int ro_insert = auth_check_permission(auth, ro_session, "test_table", PERM_INSERT);
    int ro_delete = auth_check_permission(auth, ro_session, "test_table", PERM_DELETE);
    
    test_print("Read-Only SELECT Permission", ro_select == 0);
    test_print("Read-Only INSERT Permission Denied", ro_insert != 0);
    test_print("Read-Only DELETE Permission Denied", ro_delete != 0);
    
    // Test granting specific table permissions
    int grant_result = auth_grant_table_permission(auth, "ro_user", "special_table", PERM_INSERT);
    test_print("Grant Specific Table Permission", grant_result == 0);
    
    // Test the granted permission
    int special_insert = auth_check_permission(auth, ro_session, "special_table", PERM_INSERT);
    test_print("Granted Permission Works", special_insert == 0);
    
    if (admin_select != 0 || admin_insert != 0 || admin_delete != 0 ||
        rw_select != 0 || rw_insert != 0 || rw_create == 0 ||
        ro_select != 0 || ro_insert == 0 || ro_delete == 0 ||
        grant_result != 0 || special_insert != 0) {
        success = 0;
    }
    
    printf("\nPermission statistics:\n");
    auth_print_stats(auth);
    
    auth_cleanup(auth);
    return success;
}

// Test password hashing
static int test_password_hashing() {
    printf("\n=== Testing Password Hashing ===\n");
    
    int success = 1;
    
    // Test salt generation
    char salt1[MAX_SALT_LEN], salt2[MAX_SALT_LEN];
    int salt_result1 = auth_generate_salt(salt1, MAX_SALT_LEN);
    int salt_result2 = auth_generate_salt(salt2, MAX_SALT_LEN);
    
    test_print("Salt Generation", salt_result1 == 0 && salt_result2 == 0);
    
    bool salts_different = (strcmp(salt1, salt2) != 0);
    test_print("Salts Are Different", salts_different);
    
    // Test password hashing
    char hash1[MAX_HASH_LEN], hash2[MAX_HASH_LEN];
    int hash_result1 = auth_hash_password("password123", salt1, HASH_TYPE_SHA256, hash1, MAX_HASH_LEN);
    int hash_result2 = auth_hash_password("password123", salt2, HASH_TYPE_SHA256, hash2, MAX_HASH_LEN);
    
    test_print("Password Hashing", hash_result1 == 0 && hash_result2 == 0);
    
    bool hashes_different = (strcmp(hash1, hash2) != 0);
    test_print("Same Password + Different Salt = Different Hash", hashes_different);
    
    // Test password verification
    int verify_correct = auth_verify_password("password123", hash1, salt1, HASH_TYPE_SHA256);
    int verify_wrong = auth_verify_password("wrongpassword", hash1, salt1, HASH_TYPE_SHA256);
    
    test_print("Correct Password Verification", verify_correct == 0);
    test_print("Wrong Password Rejection", verify_wrong != 0);
    
    if (salt_result1 != 0 || salt_result2 != 0 || !salts_different ||
        hash_result1 != 0 || hash_result2 != 0 || !hashes_different ||
        verify_correct != 0 || verify_wrong == 0) {
        success = 0;
    }
    
    return success;
}

// Test role-based permissions
static int test_role_permissions() {
    printf("\n=== Testing Role-Based Permissions ===\n");
    
    int success = 1;
    
    // Test getting role permissions
    uint8_t admin_perms, rw_perms, ro_perms;
    
    int admin_result = auth_get_role_permissions(ROLE_ADMIN, &admin_perms);
    int rw_result = auth_get_role_permissions(ROLE_READ_WRITE, &rw_perms);
    int ro_result = auth_get_role_permissions(ROLE_READ_ONLY, &ro_perms);
    
    test_print("Get Admin Role Permissions", admin_result == 0);
    test_print("Get Read-Write Role Permissions", rw_result == 0);
    test_print("Get Read-Only Role Permissions", ro_result == 0);
    
    // Test permission levels
    bool admin_has_all = (admin_perms & (PERM_SELECT | PERM_INSERT | PERM_DELETE | PERM_CREATE)) != 0;
    bool rw_has_basic = (rw_perms & (PERM_SELECT | PERM_INSERT | PERM_DELETE)) != 0;
    bool rw_no_create = (rw_perms & PERM_CREATE) == 0;
    bool ro_only_select = (ro_perms == PERM_SELECT);
    
    test_print("Admin Has All Permissions", admin_has_all);
    test_print("Read-Write Has Basic CRUD", rw_has_basic);
    test_print("Read-Write Cannot CREATE", rw_no_create);
    test_print("Read-Only Only SELECT", ro_only_select);
    
    // Test permission string conversion
    const char* admin_str = auth_permission_to_string(admin_perms);
    const char* ro_str = auth_permission_to_string(ro_perms);
    
    bool admin_str_ok = (strstr(admin_str, "SELECT") != NULL && strstr(admin_str, "CREATE") != NULL);
    bool ro_str_ok = (strstr(ro_str, "SELECT") != NULL && strstr(ro_str, "INSERT") == NULL);
    
    test_print("Admin Permission String", admin_str_ok);
    test_print("Read-Only Permission String", ro_str_ok);
    
    printf("Admin permissions: %s\n", admin_str);
    printf("Read-only permissions: %s\n", ro_str);
    
    if (admin_result != 0 || rw_result != 0 || ro_result != 0 ||
        !admin_has_all || !rw_has_basic || !rw_no_create || !ro_only_select ||
        !admin_str_ok || !ro_str_ok) {
        success = 0;
    }
    
    return success;
}

// Main test runner
int main() {
    printf("GuDB Authentication Manager Tests\n");
    printf("=================================\n");
    
    int total_tests = 0;
    int passed_tests = 0;
    
    // Run all test suites
    struct {
        const char* name;
        int (*test_func)(void);
    } test_suites[] = {
        {"Authentication Manager Initialization", test_auth_initialization},
        {"User Management", test_user_management},
        {"Authentication", test_authentication},
        {"Permissions", test_permissions},
        {"Password Hashing", test_password_hashing},
        {"Role-Based Permissions", test_role_permissions}
    };
    
    int num_suites = sizeof(test_suites) / sizeof(test_suites[0]);
    
    for (int i = 0; i < num_suites; i++) {
        printf("\nRunning test suite: %s\n", test_suites[i].name);
        
        int suite_result = test_suites[i].test_func();
        total_tests++;
        
        if (suite_result) {
            passed_tests++;
            printf("Suite PASSED: %s\n", test_suites[i].name);
        } else {
            printf("Suite FAILED: %s\n", test_suites[i].name);
        }
    }
    
    // Print summary
    printf("\n========================================\n");
    printf("Test Results: %d/%d suites passed\n", passed_tests, total_tests);
    
    if (passed_tests == total_tests) {
        printf("All tests PASSED!\n");
        return 0;
    } else {
        printf("Some tests FAILED!\n");
        return 1;
    }
}